iT邦幫忙

1

【kintone】Change 事件介紹與使用時的注意事項

  • 分享至 

  • xImage
  •  

在 kintone 的客製化開發中,常會透過 JavaScript API 註冊各種事件處理器(event handler),讓程式碼在特定情境下被觸發執行。kintone 提供了多種事件可供使用,這次我們要介紹主題是:在新增或編輯記錄畫面中,欄位值變更時所觸發的 app.record.create.change.<欄位代碼>app.record.edit.change.<欄位代碼> 事件。

Change 事件的行為與特性

kintone 提供以下五種類型的 change 事件:

  • 新增記錄畫面中變更欄位值時
    • 電腦版:app.record.create.change.<欄位代碼>
    • 行動版:mobile.app.record.create.change.<欄位代碼>
  • 編輯記錄畫面中變更欄位值時
    • 電腦版:app.record.edit.change.<欄位代碼>
    • 行動版:mobile.app.record.edit.change.<欄位代碼>
  • 記錄清單頁面(行內編輯)中變更欄位值時
    • 電腦版:app.record.index.edit.change.<欄位代碼>

這類事件必須指定欄位代碼,當該欄位的值在對應畫面中發生變更時,便會觸發所綁定的事件處理器。支援 change 事件的欄位類型包括:

  • 單行文字方塊
  • 數值
  • 選項按鈕
  • 核取方塊
  • 複選
  • 下拉式選單
  • 日期
  • 時間
  • 日期與時間
  • 選擇使用者
  • 選擇組織
  • 選擇群組
  • 表格(不支援記錄清單的行內編輯)

需要特別注意的是,「Lookup」和「計算」欄位本身無法觸發 change 事件。但仍可透過監聽 Lookup 所帶入的其他欄位 來判斷是否有發生變化。例如,可以設定一個數值欄位接收 Lookup 帶入的來源記錄編號,然後監聽 app.record.edit.change.<來源記錄編號的欄位代碼>,以達成類似的偵測效果。

表格欄位的 change 事件觸發時機

針對表格內部的變動,有以下兩種類型:

  • app.record.edit.change.<表格內欄位代碼>
     → 當表格中的欄位值被修改時觸發(例如使用者在表格內變更數值或下拉選單內容)。
  • app.record.edit.change.<表格本身欄位代碼>
     → 當整個表格新增或刪除列時觸發(例如點擊「新增一列」或刪除某列)。

Change 事件不支援非同步函式的限制

在 kintone 的客製化中,我們經常會遇到需要透過 API 取得資料的非同步處理情境。舉個簡單的例子:
假設在儲存記錄時需要自動產生一組流水號,可以這樣實作:

kintone.events.on('app.record.create.submit', async event => {
  const { record } = event

  // 透過 API 取得最新一筆記錄
  const res = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
    app: kintone.app.getId(),
    query: 'order by $id desc limit 1',
  })

  const lastNum = res.records.length > 0 ? Number(res.records[0].流水號.value) : 0
  record.流水號.value = String(lastNum + 1).padStart(8, '0')

  return event
})

這段程式碼中,我們在 app.record.create.submit 事件中使用了 async 函式搭配 await 來處理非同步資料取得,是被支援的寫法。

然而,如果在欄位變更時也想要取得資料,例如使用者在 Lookup 欄位帶入資料後,自動從來源應用程式取得表格資料並帶入當前記錄,就會遇到一個常見問題。

你可能會這樣寫:

kintone.events.on('app.record.edit.change.來源記錄號碼', async event => {
  const { record } = event
  const recordId = record.來源記錄號碼.value

  const res = await kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {
    app: 來源應用程式ID,
    id: recordId,
  })

  // 將來源記錄的表格資料帶入當前記錄
  record.當前表格.value = res.record.來源表格.value

  return event
})

這時就會遇到以下錯誤訊息:

Uncaught Error: app.record.edit.change.來源記錄號碼 is not allowed to return "Thenable" object.

這是因為 change 事件的事件處理器不支援 async 函式或回傳 Promise(即 Thenable 物件)。也就是說,不能在 change 事件中直接使用 async/await 或回傳一個包含非同步操作的處理流程。

避開限制的方法

既然 change 事件不允許回傳 Promise,那該如何實作需要非同步取得資料的需求呢?以下提供兩種常見作法:

方法一:使用 setTimeout() 包裹非同步邏輯

可以將非同步處理的部分放進 setTimeout 中,讓事件處理器本身仍保持同步,避免觸發錯誤。

kintone.events.on('app.record.edit.change.來源記錄號碼', event => {
  const recordId = event.record.來源記錄號碼.value

  setTimeout(async () => {
    if (!recordId) return

    try {
      const res = await kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {
        app: 來源應用程式ID,
        id: recordId,
      })

      const { record } = kintone.app.record.get()
      record.當前表格.value = res.record.來源表格.value
      kintone.app.record.set({ record })
    } catch (error) {
      console.error('取得來源資料失敗', error)
    }
  }, 0)

  return event
})

✅ 優點:

  • 寫法直覺,能使用 async/await
  • 與一般非同步流程一致,便於閱讀與維護

⚠️ 注意:

  • 需要額外呼叫 kintone.app.record.set() 更新畫面資料
  • 屬於 macrotask,執行時機會比 .then() 略晚

方法二:使用 Promise .then() 處理流程

另一種方式是不使用 async/await,改用 .then() 語法,讓事件處理器仍為同步函式。callback 會被排入 microtask 隊列,在同步程式碼結束後立即執行。

kintone.events.on('app.record.edit.change.來源記錄號碼', event => {
  const recordId = event.record.來源記錄號碼.value

  if (!recordId) return event

  kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {
    app: 來源應用程式ID,
    id: recordId,
  }).then(res => {
    const { record } = kintone.app.record.get()
    record.當前表格.value = res.record.來源表格.value
    kintone.app.record.set({ record })
  }).catch(error => {
    console.error('取得來源資料失敗', error)
  })

  return event
})

✅ 優點:

  • 不會觸發事件回傳 Promise 的錯誤
  • callback 屬於 microtask,執行時機比 setTimeout 更早

⚠️ 注意:

  • 需要額外呼叫 kintone.app.record.set() 更新畫面資料
  • .then() 的錯誤需用 .catch() 明確捕捉
  • 程式結構相較 async/await 稍微不直覺。當邏輯較多、需串接多個 API 時,寫法容易變得巢狀、難以閱讀與維護

不論是使用 setTimeout.then(),這類處理邏輯其實都已跳脫原本的 change 事件處理器本體,因此無法再透過 event.record 直接修改欄位資料。若要更新畫面上的欄位內容,需改用 kintone.app.record.set(),並建議搭配 kintone.app.record.get() 先取得最新畫面資料後再進行修改,以減少資料被其他事件處理器覆蓋的風險。

延遲處理的風險與結語

實務上,無論是使用 setTimeout.then(),都存在一定的風險。這些方式本質上都是「延遲處理」,一旦應用中有較多客製化邏輯、註冊了多個事件處理器,就很難保證延後執行的程式不會與其他處理器產生衝突。例如可能出現彼此覆蓋欄位值、寫入時機錯亂等問題,特別是在多段程式都操作同一個欄位時更容易發生。

因此,雖然這些解法可以避開 change 事件不支援非同步的限制,但仍需謹慎評估使用時機。若流程較複雜或與其他邏輯高度耦合,建議重新思考整體設計,是否能將資料處理延後至 submit 階段,或集中於單一事件處理器中執行,以減少潛在衝突,提升可維護性。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言